﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;

namespace Pahan.McuFusion.Common
{
    public static class Assert
    {
        #region Object assertions

        public static void NotNull(Object arg, String argName)
        {
            if (arg == null)
            {
                throw new ArgumentNullException(argName);
            }
        }

        //public static void In<T>(T arg, String argName, IEnumerable<T> values)
        //{
        //    if (!values.Contains(arg))
        //    {
        //        throw new ArgumentOutOfRangeException(String.Format(
        //            "The value of {0} must be in the following list: {1}.",
        //            argName, String.Join(", ",
        //                values.Select(value => value.ToString()).ToArray())));
        //    }
        //}

        //public static void NotIn<T>(T arg, String argName, IEnumerable<T> values)
        //{
        //    if (values.Contains(arg))
        //    {
        //        throw new ArgumentOutOfRangeException(String.Format(
        //            "The value of {0} must not be in the following list: {1}.",
        //            argName, String.Join(", ",
        //                values.Select(value => value.ToString()).ToArray())));
        //    }
        //}

        //public static void In<T>(T arg, String argName, params T[] values)
        //{
        //    if (!values.Contains(arg))
        //    {
        //        throw new ArgumentOutOfRangeException(String.Format(
        //            "The value of {0} must be in the following list: {1}.",
        //            argName, String.Join(", ",
        //                values.Select(value => value.ToString()).ToArray())));
        //    }
        //}

        //public static void NotIn<T>(T arg, String argName, params T[] values)
        //{
        //    if (values.Contains(arg))
        //    {
        //        throw new ArgumentOutOfRangeException(String.Format(
        //            "The value of {0} must not be in the following list: {1}.",
        //            argName, String.Join(", ",
        //                values.Select(value => value.ToString()).ToArray())));
        //    }
        //}

        public static void Equal(Object arg, String argName, Object value)
        {
            if (!arg.Equals(value))
            {
                throw new ArgumentOutOfRangeException(String.Format(
                    "The value of {0} must be equal to {1}.",
                    argName, value));
            }
        }

        public static void NotEqual(Object arg, String argName, Object value)
        {
            if (arg.Equals(value))
            {
                throw new ArgumentOutOfRangeException(String.Format(
                    "The value of {0} must not be equal to {1}.",
                    argName, value));
            }
        }

        public static void Equal(Object arg1, String arg1Name, Object arg2, String arg2Name)
        {
            if (!arg1.Equals(arg2))
            {
                throw new ArgumentOutOfRangeException(String.Format(
                    "The value of {0} must be equal to the value of {1}.",
                    arg1Name, arg2Name));
            }
        }

        public static void NotEqual(Object arg1, String arg1Name, Object arg2, String arg2Name)
        {
            if (arg1.Equals(arg2))
            {
                throw new ArgumentOutOfRangeException(String.Format(
                    "The value of {0} must not be equal to the value of {1}.",
                    arg1Name, arg2Name));
            }
        }

        public static void OfType(Object arg, String argName, Type type)
        {
            if (!type.IsInstanceOfType(arg))
            {
                throw new ArgumentException(String.Format(
                    "The type of {0} must be {1}.",
                    arg, type.FullName));
            }
        }

        public static void NotOfType(Object arg, String argName, Type type)
        {
            if (type.IsInstanceOfType(arg))
            {
                throw new ArgumentException(String.Format(
                    "The type of {0} must not be {1}.",
                    arg, type.FullName));
            }
        }

        public static void OfType<T>(Object arg, String argName)
        {
            Type type = typeof(T);
            if (!type.IsInstanceOfType(arg))
            {
                throw new ArgumentException(String.Format(
                    "The type of {0} must be {1}.",
                    arg, type.FullName));
            }
        }

        public static void NotOfType<T>(Object arg, String argName)
        {
            Type type = typeof(T);
            if (type.IsInstanceOfType(arg))
            {
                throw new ArgumentException(String.Format(
                    "The type of {0} must not be {1}.",
                    arg, type.FullName));
            }
        }

        #endregion

        #region String assertions

        public static void NotEmpty(String arg, String argName)
        {
            if (arg == String.Empty)
            {
                throw new ArgumentException("The string is empty.", argName);
            }
        }

        public static void Empty(String arg, String argName)
        {
            if (arg != String.Empty)
            {
                throw new ArgumentException("The string is not empty.", argName);
            }
        }

        public static void NotNullOrEmpty(String arg, String argName)
        {
            if (arg == null)
            {
                throw new ArgumentNullException(argName);
            }
            if (arg == String.Empty)
            {
                throw new ArgumentException("The string is empty.", argName);
            }
        }

        public static void NullOrEmpty(String arg, String argName)
        {
            if (!String.IsNullOrEmpty(arg))
            {
                throw new ArgumentException("The string is neither null nor empty.", argName);
            }
        }

        public static void MaxLength(String arg, String argName, int maxLength)
        {
            if (arg.Length > maxLength)
            {
                throw new ArgumentException("The string is too long.", argName);
            }
        }

        public static void LengthEqual(String arg1, String arg1Name, String arg2, String arg2Name)
        {
            if (arg1.Length != arg2.Length)
            {
                throw new ArgumentException(String.Format(
                    "The length of the '{0}' argument is not equal to the length of the '{1}' argument.",
                    arg1Name, arg2Name));
            }
        }

        #endregion

        #region Collection assertions

        public static void Empty(ICollection arg, String argName)
        {
            if (arg.Count > 0)
            {
                throw new ArgumentException("The collection is not empty.", argName);
            }
        }

        public static void NotNullOrEmpty(ICollection arg, String argName)
        {
            if (arg == null)
            {
                throw new ArgumentNullException(argName);
            }
            if (arg.Count == 0)
            {
                throw new ArgumentException("The collection is empty.", argName);
            }
        }

        public static void CountEqual(ICollection arg1, String arg1Name, ICollection arg2, String arg2Name)
        {
            if (arg1.Count != arg2.Count)
            {
                throw new ArgumentException(String.Format(
                    "The length of the '{0}' argument is not equal to the length of the '{1}' argument.",
                    arg1Name, arg2Name));
            }
        }

        #endregion

        #region Collection<T> assertions

        public static void Empty<T>(ICollection<T> arg, String argName)
        {
            if (arg.Count > 0)
            {
                throw new ArgumentException("The collection is not empty.", argName);
            }
        }

        public static void NotNullOrEmpty<T>(ICollection<T> arg, String argName)
        {
            if (arg == null)
            {
                throw new ArgumentNullException(argName);
            }
            if (arg.Count == 0)
            {
                throw new ArgumentException("The collection is empty.", argName);
            }
        }

        public static void CountEqual<T>(ICollection<T> arg1, String arg1Name, ICollection<T> arg2, String arg2Name)
        {
            if (arg1.Count != arg2.Count)
            {
                throw new ArgumentException(String.Format(
                    "The length of the '{0}' argument is not equal to the length of the '{1}' argument.",
                    arg1Name, arg2Name));
            }
        }

        #endregion

        #region IEnumerable<T> assertions.

        //public static void All<T>(IEnumerable<T> arg, String argName, Func<T, bool> predicate)
        //{
        //    if (!arg.All(predicate))
        //    {
        //        throw new ArgumentException("Some elements do not match the condition.", argName);
        //    }
        //}

        //public static void Any<T>(IEnumerable<T> arg, String argName, Func<T, bool> predicate)
        //{
        //    if (!arg.Any(predicate))
        //    {
        //        throw new ArgumentException("No elements match the condition.", argName);
        //    }
        //}

        #endregion

        #region IEquatable<T> assertions

        public static void Equal<T>(IEquatable<T> arg, String argName, T value)
        {
            if (!arg.Equals(value))
            {
                throw new ArgumentOutOfRangeException(String.Format(
                    "The value of {0} must be equal to {1}.",
                    argName, value));
            }
        }

        public static void NotEqual<T>(IEquatable<T> arg, String argName, T value)
        {
            if (arg.Equals(value))
            {
                throw new ArgumentOutOfRangeException(String.Format(
                    "The value of {0} must not be equal to {1}.",
                    argName, value));
            }
        }

        public static void Equal<T>(IEquatable<T> arg1, String arg1Name, T arg2, String arg2Name)
        {
            if (!arg1.Equals(arg2))
            {
                throw new ArgumentOutOfRangeException(String.Format(
                    "The value of {0} must be equal to the value of {1}.",
                    arg1Name, arg2Name));
            }
        }

        public static void NotEqual<T>(IEquatable<T> arg1, String arg1Name, T arg2, String arg2Name)
        {
            if (arg1.Equals(arg2))
            {
                throw new ArgumentOutOfRangeException(String.Format(
                    "The value of {0} must not be equal to the value of {1}.",
                    arg1Name, arg2Name));
            }
        }

        #endregion

        #region IComparable<T> assertions

        public static void Greater<T>(IComparable<T> arg, String argName, T value)
        {
            if (arg.CompareTo(value) <= 0)
            {
                throw new ArgumentOutOfRangeException(String.Format(
                    "The value of {0} must be greater than {1}.",
                    argName, value));
            }
        }

        public static void Less<T>(IComparable<T> arg, String argName, T value)
        {
            if (arg.CompareTo(value) >= 0)
            {
                throw new ArgumentOutOfRangeException(String.Format(
                    "The value of {0} must be less than {1}.",
                    argName, value));
            }
        }

        public static void GreaterOrEqual<T>(IComparable<T> arg, String argName, T value)
        {
            if (arg.CompareTo(value) < 0)
            {
                throw new ArgumentOutOfRangeException(String.Format(
                    "The value of {0} must be greater than or equal to {1}.",
                    argName, value));
            }
        }

        public static void LessOrEqual<T>(IComparable<T> arg, String argName, T value)
        {
            if (arg.CompareTo(value) > 0)
            {
                throw new ArgumentOutOfRangeException(String.Format(
                    "The value of {0} must be less than or equal to {1}.",
                    argName, value));
            }
        }

        public static void Greater<T>(IComparable<T> arg1, String arg1Name, T arg2, String arg2Name)
        {
            if (arg1.CompareTo(arg2) <= 0)
            {
                throw new ArgumentOutOfRangeException(String.Format(
                    "The value of {0} must be greater than the value of {1}.",
                    arg1Name, arg2Name));
            }
        }

        public static void Less<T>(IComparable<T> arg1, String arg1Name, T arg2, String arg2Name)
        {
            if (arg1.CompareTo(arg2) >= 0)
            {
                throw new ArgumentOutOfRangeException(String.Format(
                    "The value of {0} must be less than the value of {1}.",
                    arg1Name, arg2Name));
            }
        }

        public static void GreaterOrEqual<T>(IComparable<T> arg1, String arg1Name, T arg2, String arg2Name)
        {
            if (arg1.CompareTo(arg2) < 0)
            {
                throw new ArgumentOutOfRangeException(String.Format(
                    "The value of {0} must be greater than or equal to the value of {1}.",
                    arg1Name, arg2Name));
            }
        }

        public static void LessOrEqual<T>(IComparable<T> arg1, String arg1Name, T arg2, String arg2Name)
        {
            if (arg1.CompareTo(arg2) > 0)
            {
                throw new ArgumentOutOfRangeException(String.Format(
                    "The value of {0} must be less than or equal to the value of {1}.",
                    arg1Name, arg2Name));
            }
        }

        #endregion

        #region Custom Assertions

        public static void IsTrue(bool condition, String message)
        {
            if (!condition)
            {
                throw new InvalidOperationException(message);
            }
        }

        public static void IsFalse(bool condition, String message)
        {
            if (condition)
            {
                throw new InvalidOperationException(message);
            }
        }

        #endregion
    }
}